﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using FunMazeEngine;
using System.Runtime.Remoting;
using System.Threading;
using MazeEntity;

namespace GameServer
{
    [Serializable]
    public class Mediator : MarshalByRefObject, IMediator
    {
        string broadcast4 = "";
        object locker = new object();
        Game game;
        bool gameStart = false;
        int totalcells = 0;
        int mapsize = 0;
        string errorMessage = string.Empty;
        string message = string.Empty;

        bool isBackUpServerSet = false;       

        #region IMediator Members

        public bool CreateGame(IGameClient clientObject, int mazePara, int treasureCount)
        {
            try
            {
                Random random = new Random();
                Map gameMap = new Map();
                gameMap.CreateMap(mazePara, treasureCount);
                mapsize = mazePara;

                game = new Game();
                game.GameMap = gameMap;         
                
                game.AddPlayer(clientObject);
                totalcells = gameMap.GetTotalCells();
                int spawnlocation = random.Next(0,totalcells-1);
                clientObject.Player.Location = spawnlocation;
                int thisrow, thiscolumn = 0;
                game.occupiedlist.Add(spawnlocation);
                game.GameMap.GetRowColumnLocation(clientObject.Player.Location, out thisrow, out thiscolumn);
                game.GameMap.PlayerGetTreasure(clientObject.Player, thisrow, thiscolumn);
             
                message = "Server: new game created. Welcome player!";
                this.SendWelcomeMessageToPlayers(message);                        
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.StackTrace);
                return false;

            }
        }

        public bool GameExists()
        {
            if (game.Equals(null)) return false;
            else return true;
        }

        public bool StartGame()
        {
            Thread.Sleep(20000);
            gameStart = true;
            message = "Hello players, game is started. You can play now. Enjoy!";
            this.SendWelcomeMessageToPlayers(message);
            return gameStart;
        }

        public bool JoinGame(IGameClient clientObject)
        {
            bool joinGameResult = false;

            if (game.Equals(null))
            {
                errorMessage = "No game exist";
                return false;
            }

            try
            {
                {
                    if (!gameStart)
                    {
                        Random random = new Random();
                        joinGameResult = game.AddPlayer(clientObject);

                        if (joinGameResult)
                        {
                            clientObject.Player.Location = -1;

                            while (clientObject.Player.Location == -1)
                            {
                                int spawnloc = random.Next(0, totalcells);
                                if (game.PlayerThere(spawnloc) == false)
                                {
                                    clientObject.Player.Location = spawnloc;
                                    game.occupiedlist.Add(spawnloc);
                                }                             
                            }
                            int thisrow, thiscolumn = 0;
                            game.GameMap.GetRowColumnLocation(clientObject.Player.Location, out thisrow, out thiscolumn);
                            game.GameMap.PlayerGetTreasure(clientObject.Player, thisrow, thiscolumn);

                            if (!isBackUpServerSet)
                            {
                                isBackUpServerSet = true;
                                game.BackUpServerAddress = clientObject.Player.PlayerIPAddress;
                                clientObject.Player.IsBackUpHost = true;
                                this.SendBackUpServerIPToPlayers(game.BackUpServerAddress);
                                clientObject.StartBackUpGameServer(game);
                                this.SendGameInfoToBackUpServer();
                                
                            }
                            else if(isBackUpServerSet)
                            {
                                this.SendBackUpServerIPToPlayers(game.BackUpServerAddress);
                                this.SendGameInfoToBackUpServer();
                            }

                            

                            message = clientObject.Player.Name + " joins the Game";
                            SendWelcomeMessageToPlayers(message);                            
                            joinGameResult = true;
                        }
                    }
                    else
                    {
                        clientObject.SendWelcomeMessageToPlayer("Game already started. Please create new game or join other existing game");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.StackTrace);
            }
            return joinGameResult;
        }

        public IGameClient GetGamePlayer(string name)
        {
            return game.RetrieveGamePlayer(name);
        }

        public void ExitGame(IGameClient clientObject)
        {
            game.RemovePlayer(clientObject); throw new NotImplementedException();
        }

        public int PlayerMove(IGameClient clientObject, char x)
        {
                string direction = "";
                int moveSuccess = -1;
                if (gameStart)
                {
                    int currentLocation = clientObject.Player.Location;
                    int row = 0, column = 0;
                    game.GameMap.GetRowColumnLocation(currentLocation, out row, out column);

                    if (x == 'E')
                    {
                        if (row == (mapsize - 1)) { moveSuccess = 0; }
                        else
                        {
                            row++;
                            direction = "East";
                        }
                    }

                    else if (x == 'W')
                    {

                        if (row == 0) { moveSuccess = 0; }
                        else
                        {
                            row--;
                            direction = "West";
                        }

                    }
                    else if (x == 'N')
                    {

                        if (column == 0) { moveSuccess = 0; }
                        else
                        {
                            column--;
                            direction = "North";
                        }
                    }
                    else
                    {

                        if (column == (mapsize - 1)) { moveSuccess = 0; }
                        else
                        {
                            column++;
                            direction = "South";
                        }
                    }
                    int newlocation = game.GameMap.GetCellLocation(row, column);
                    if (game.PlayerThere(newlocation))
                    {
                        if (moveSuccess == 0) { }
                        else
                        {
                            moveSuccess = 1;
                        }
                    }
                    else
                    {
                        int TreasureObtained = 0;
                        clientObject.Player.Location = game.GameMap.GetCellLocation(row, column);
                        game.occupiedlist.Remove(currentLocation);
                        game.occupiedlist.Add(newlocation);
                        moveSuccess = 2;
                        if (game.GameMap.numberoftreasure(row, column) != 0)
                        {
                            TreasureObtained = game.GameMap.PlayerGetTreasure(clientObject.Player, row, column);
                        }
                        List<IGameClient> newplayerlist = new List<IGameClient>();

                        string broadcast1 = "Player Locations: \r\n";
                        string broadcast2 = "";
                        string broadcast3 = "Treasure Locations: \r\n";
                        foreach (IGameClient client in game.gamePlayerList)
                        {
                            if (IsPlayerAlive(client))
                            {
                                newplayerlist.Add(client);
                                int newrow, newcol = 0;
                                game.GameMap.GetRowColumnLocation(client.Player.Location, out newrow, out newcol);
                                broadcast1 = broadcast1 + client.Player.Name + "\t(" + Convert.ToString(newrow) + "," + Convert.ToString(newcol) + ")\t" + client.Player.TotalCollectedTreasure + "\r\n";
                                broadcast2 = broadcast2 + clientObject.Player.Name + " has moved " + direction + " and collected " + Convert.ToString(TreasureObtained) + " Treasure.\r\n";
                                if (TreasureObtained != 0) { broadcast2 = broadcast2 + clientObject.Player.Name + " currently has " + clientObject.Player.TotalCollectedTreasure + " Treasure.\r\n"; }
                                broadcast2 = broadcast2 + clientObject.Player.Name + " is now at (" + Convert.ToString(row) + "," + Convert.ToString(column) + ").\r\n";
                            }
                        }

                        for (int x1 = 0; x1 < game.GameMap.rows; x1++)
                        {
                            for (int y1 = 0; y1 < game.GameMap.columns; y1++)
                            {
                                int count = game.GameMap.numberoftreasure(x1, y1);
                                if (count != 0)
                                {
                                    broadcast3 = broadcast3 + "(" + Convert.ToString(x1) + "," + Convert.ToString(y1) + ") \t\t Treasures:" + Convert.ToString(count) + "\r\n";
                                }
                            }
                        }
                        game.gamePlayerList = newplayerlist;

                        if (broadcast4 == "")
                        {

                            if (broadcast3 == "Treasure Locations: \r\n")
                            {
                                int maxcount = 0;
                                List<IGameClient> winnerlist = new List<IGameClient>();
                                List<IGameClient> finallist = new List<IGameClient>();
                                foreach (IGameClient client in game.gamePlayerList)
                                {
                                    if (client.Player.TotalCollectedTreasure >= maxcount)
                                    {
                                        if (client.Player.TotalCollectedTreasure == maxcount)
                                        {
                                            broadcast4 = broadcast4 + client.Player.Name + " is a winner!\r\n";
                                        }
                                        else { maxcount = client.Player.TotalCollectedTreasure; broadcast4 = "Game Over! \r\n\r\n\r\n" + client.Player.Name + " is a winner! \r\n"; }
                                    }
                                }
                            }
                        }



                        foreach (IGameClient client in game.gamePlayerList)
                        {
                            if (broadcast4 == "")
                            {
                                client.SendWelcomeMessageToPlayer(broadcast1 + broadcast3 + broadcast2);
                            }
                            else { client.SendWelcomeMessageToPlayer(broadcast4); }
                        }
                    }
                    if (isBackUpServerSet)
                    {
                        this.SendGameInfoToBackUpServer();
                    }
                }
                else
                {
                    clientObject.SendWelcomeMessageToPlayer("Game haven't started. Please wait...");
                }

                return moveSuccess;
            
        }

        public void SendBackUpServerIPToPlayers(string serverIP)
        {            
            foreach (IGameClient player in game.gamePlayerList)
            {
                try
                {

                    player.SendBackUpServerIPToPlayer(serverIP);  
                }
                catch
                {
                    RemoveCrashPlayer();
                }
              
            }            
        }

        public void SendWelcomeMessageToPlayers(string message)
        {
            foreach (IGameClient player in game.gamePlayerList)
            {
                try
                {
                    player.SendWelcomeMessageToPlayer(message);
                }
                catch
                {

                    RemoveCrashPlayer();
                }
               
            }
        }

        public void SendGameInfoToBackUpServer()
        {           
            try
            {
                IGameClient player = game.gamePlayerList.First(p => p.Player.IsBackUpHost == true);
                player.GameInfoUpdate(this.game);
            }
            catch
            {
                RemoveCrashPlayer();
            }           
        }        
        
        public string GetErrorMessage()
        {
            return this.errorMessage;
        }

        public void SwitchToPrimaryServer(GameEntity theGameInfo, IGameClient client)
        {
            try
            {
                Game gameInfo = new Game();
                Map gameMap = new Map();
                MapEntity mapEntity = theGameInfo.GameMap;
                gameMap.cellNum = mapEntity.cellNum;
                gameMap.columns = mapEntity.columns;
                gameMap.map = mapEntity.map;
                gameMap.rows = mapEntity.rows;
                gameMap.treasureCount = mapEntity.treasureCount;
                gameMap.treasureList = mapEntity.treasureList;

                gameInfo.GameMap = gameMap;

                List<IGameClient> clientList = new List<IGameClient>();
                List<GamePlayer> playerList = new List<GamePlayer>();
                foreach (GamePlayerEntity player in theGameInfo.PlayerList)
                {
                    GamePlayer gamePlayer = new GamePlayer(player.Name);
                    gamePlayer.Move = player.Move;
                    gamePlayer.Location = player.Location;                  
                    gamePlayer.IsPrimaryHost = player.IsPrimaryHost;
                    gamePlayer.IsBackUpHost = player.IsBackUpHost;
                    gamePlayer.IsAlive = player.IsAlive;
                    gamePlayer.PlayerIPAddress = player.PlayerIPAddress;
                    gamePlayer.TotalCollectedTreasure = player.TotalCollectedTreasure;

                    if (player.Name == client.Player.Name)
                    {
                        gamePlayer.IsPrimaryHost = true;
                        gamePlayer.IsBackUpHost = false;

                        client.Player.IsPrimaryHost = true;
                        client.Player.IsBackUpHost = false;
                        clientList.Add(client);
                    }
                    playerList.Add(gamePlayer);
                }


                gameInfo.occupiedlist = theGameInfo.occupiedlist;
                gameInfo.PlayerList = clientList;
                gameInfo.PlayerEntityList = playerList;

                this.game = gameInfo;
                this.gameStart = true;                
            }
            catch
            {                
                
            }
            
        }

        public void UpdateGameInfo(GameEntity theGameInfo)
        {
            theGameInfo.UpdateGameName("Hello World");
        }

        public void UpdateClientList(IGameClient client)
        {
            foreach (GamePlayer player in this.game.PlayerEntityList)
            {
                if (player.Name == client.Player.Name)
                {
                    client.Player.Location = player.Location;
                    client.Player.Move = player.Move;
                    client.Player.Location = player.Location;
                    client.Player.IsPrimaryHost = player.IsPrimaryHost;
                    client.Player.IsBackUpHost = player.IsBackUpHost;
                    client.Player.IsAlive = player.IsAlive;
                    client.Player.PlayerIPAddress = player.PlayerIPAddress;
                    client.Player.TotalCollectedTreasure = player.TotalCollectedTreasure;

                    if (!isBackUpServerSet)
                    {
                        StartBackUpServer(this.game);
                        isBackUpServerSet = true;
                        client.Player.IsBackUpHost = true;
                    }

                    this.game.PlayerList.Add(client);
                }
            }
        }
        #endregion

        private void StartBackUpServer(Game game)
        {
            foreach (IGameClient player in game.gamePlayerList)
            {
                try
                {
                    if (player.Player.IsAlive && !player.Player.IsPrimaryHost)
                    {
                        player.Player.IsBackUpHost = true;
                        game.BackUpServerAddress = player.Player.PlayerIPAddress;
                        this.SendBackUpServerIPToPlayers(game.BackUpServerAddress);
                        player.StartBackUpGameServer(game);
                        isBackUpServerSet = true;
                    }
                }
                catch
                {
                    IsPlayerAlive(player);
                }
               
            }
        }

        private bool IsPlayerAlive(IGameClient client)
        {
            bool isAlive = false;
            try
            {
                isAlive = client.IsPlayerAlive();
                client.Player.IsAlive = isAlive;
            }
            catch
            {
                isAlive = false;                
            }
          
            return isAlive;
        }

        private void RemoveCrashPlayer()
        {
            try
            {
                bool isBackUpServerAlive = false;
                List<IGameClient> updatePlayerList = new List<IGameClient>();
                foreach (IGameClient player in this.game.PlayerList)
                {
                    if (IsPlayerAlive(player))
                    {
                        if (player.Player.IsBackUpHost)
                        {
                            isBackUpServerAlive = true;
                        }
                        updatePlayerList.Add(player);
                    }
                }

                this.game.PlayerList = updatePlayerList;

                if (!isBackUpServerAlive)
                {
                    IGameClient client = this.game.PlayerList.FirstOrDefault(p => p.Player.IsPrimaryHost == false);
                    client.StartBackUpGameServer(this.game);
                    client.Player.IsBackUpHost = true;
                    isBackUpServerSet = true;
                }
            }
            catch
            {
                isBackUpServerSet = false;
            }
            
        }
    }
}
